/***************************************************************************************
* Copyright (c) 2014-2022 Zihao Yu, Nanjing University
*
* NEMU is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*          http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*
* See the Mulan PSL v2 for more details.
***************************************************************************************/

#include "sdb.h"

#define NR_WP 32
#ifdef CONFIG_WATCHPOINT
static WP wp_pool[NR_WP] = {};
static WP *head = NULL, *free_ = NULL;

void init_wp_pool() {
  int i;
  for (i = 0; i < NR_WP; i ++) {
    wp_pool[i].NO = i;
    wp_pool[i].next = (i == NR_WP - 1 ? NULL : &wp_pool[i + 1]);
  }

  head = NULL;
  free_ = wp_pool;
}

/* TODO: Implement the functionality of watchpoint */
//return next free_
void new_wp(long res,char* expression){
	//record now and next pointer
	WP* now = NULL;
	now = free_;
	//find_free_one
	if(free_==NULL) {printf("failed! no more watchpoint can be set\n");return;}
	//set data
	free_->res = res;
	memset(free_->expression,'\0',strlen(free_->expression));
	memcpy(free_->expression,expression,strlen(expression));
	free_=free_->next;
	//refresh head
	//prepared to add now to the end of the head link
	now->next =NULL;
	//head_link is empty
	if(head == NULL) head = now; 
	//add item in the used link
	else {
		//find the last item of the head link
		WP* last_head = head;
		while(1){
			if(last_head->next == NULL)break;
			else last_head = last_head ->next;
		}
		last_head -> next = now; 
	} 
	//print information
	printf("Watchpoint NO: %d\texpression: %s\n",now->NO,now->expression);	
}

int free_wp(WP*wp){
	//find farget
	bool if_find = false;
	if(head == NULL){printf("Watchpoint have't be set yet\n");return 0;}
	//checkout if wp is in head link list
	//and record the item before it
	WP* before = NULL;
	WP* now = head;
	while(1)
	{
		if(now == wp){if_find = true;break;}
		if(now->next == NULL)break;	
		before = now;
		now = now->next;
	}
	if (if_find == false)return 0;
	
	//move the wp and connect the head link list
	if(before == NULL) head = wp->next;
	else before->next = wp->next;	
	
	//add item in free_ link list
	wp->next = NULL;
	WP* last = free_;
	while (1)
	{
		if(last->next == NULL)break;
		last = last->next;
	}	
	last->next = wp;
	wp->res = 0;
	return 0;
}
//dispaly watchpoints is set
void info_wp(void)
{
WP *last = head;	
if(head == NULL) {printf("haven't set watchpoint yet\n");return;}
while(1)
{
 printf("Watchpoint  NO: %d\texpression: %s\n",last->NO,last->expression);
 if(last->next == NULL) break;
 else last = last->next;
}
}

void free_wp_num(int num){
	if(head == NULL){printf("Watchpoint NO: %d have't be set yet\n",num);return;}
	if(num <0 || num >NR_WP-1){printf("n is too big");return;}
	WP* last = head;
	while(1){
		if(last->NO == num)
		{
			free_wp(last);
			info_wp();
			return;	
		}
		if(last->next == NULL)break;
		last = last->next;
	}
	printf("Watchpoint NO: %d have't be set yet\n",num);
}

int monitor_wp(void)
{
	int num = 0;
	WP* now = head;
	if(head == NULL) return 0;
	while(1)
	{
		bool success = true;
		long result = expr(now->expression,&success);	
		if(result != now->res){
			num ++;
 			printf("Watchpoint  NO: %d\texpression: %s\t last_res: %ld\t new_res: %ld\n",now->NO,now->expression,now->res,result);
			now->res = result;
		}	
		if(now->next == NULL)break;	
		now = now->next;
	}	
	if (num >0) return 1;
	return 0;
}
#endif
